/**
 **************************************************************************
 * @file     main.c
 * @brief    main program
 **************************************************************************
 *                       Copyright notice & Disclaimer
 *
 * The software Board Support Package (BSP) that is made available to
 * download from Artery official website is the copyrighted work of Artery.
 * Artery authorizes customers to use, copy, and distribute the BSP
 * software and its related documentation for the purpose of design and
 * development in conjunction with Artery microcontrollers. Use of the
 * software is governed by this copyright notice and the following disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
 * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
 * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
 * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
 * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
 *
 **************************************************************************
 */


#include "bootloader.h"
#include "at32_mcu_info.h"
#include <string.h>
/** @addtogroup AT32F413_periph_examples
 * @{
 */

/** @addtogroup 413_USB_device_vcp_loopback USB_device_vcp_loopback
 * @{
 */
// #define usart_buffer_size 4086
const firmware_info_t zino_firemware_info __attribute__((section("firemware_info"))) = {
	.name = "AT32F413RCT7-ZINO-VirtualComport-bootloader-V1.0.0-20241115",
	.version = 0x10000000,
	.magic = FLASH_FW_MAGIC,
	.start_addr = 0x08004000,
	.pid = PID2INT(0, 0, 0, 0),
	.date = DATE2INT(2024, 11, 15),
	.DeviceID = AT32F421C8T7_64KB_LQFP48,

};
usbd_core_type usb_core_dev;
// uint8_t usb_buffer[usart_buffer_size];
/* usart global struct define */
extern linecoding_type linecoding;
void usb_usart_config(linecoding_type linecoding);
// void usart_gpio_config(void);

// uint8_t usart_rx_buffer[usart_buffer_size];
// uint16_t hw_usart_rx_index = 0;
// uint16_t hw_usart_read_index = 0;
// uint16_t usart_rx_data_len = 0;
// uint16_t ov_cnt = 0;
// void usart_send_data(uint8_t *send_data, uint16_t len);
// uint16_t usart_receive_data(void);

/**
 * @brief  usb 48M clock select
 * @param  clk_s:USB_CLK_HICK, USB_CLK_HEXT
 * @retval none
 */
void usb_clock48m_select(usb_clk48_s clk_s)
{
	if (clk_s == USB_CLK_HICK)
	{
		crm_usb_clock_source_select(CRM_USB_CLOCK_SOURCE_HICK);

		/* enable the acc calibration ready interrupt */
		crm_periph_clock_enable(CRM_ACC_PERIPH_CLOCK, TRUE);

		/* update the c1\c2\c3 value */
		acc_write_c1(7980);
		acc_write_c2(8000);
		acc_write_c3(8020);

		/* open acc calibration */
		acc_calibration_mode_enable(ACC_CAL_HICKTRIM, TRUE);
	}
	else
	{
		switch (system_core_clock)
		{
		/* 48MHz */
		case 48000000:
			crm_usb_clock_div_set(CRM_USB_DIV_1);
			break;

		/* 72MHz */
		case 72000000:
			crm_usb_clock_div_set(CRM_USB_DIV_1_5);
			break;

		/* 96MHz */
		case 96000000:
			crm_usb_clock_div_set(CRM_USB_DIV_2);
			break;

		/* 120MHz */
		case 120000000:
			crm_usb_clock_div_set(CRM_USB_DIV_2_5);
			break;

		/* 144MHz */
		case 144000000:
			crm_usb_clock_div_set(CRM_USB_DIV_3);
			break;

		/* 168MHz */
		case 168000000:
			crm_usb_clock_div_set(CRM_USB_DIV_3_5);
			break;

		/* 192MHz */
		case 192000000:
			crm_usb_clock_div_set(CRM_USB_DIV_4);
			break;

		default:
			break;
		}
	}
}
uint16_t usb_vcp_read(uint8_t *buffer)
{
	uint16_t len = 0;
	uint32_t timeout = millis();
	while (1)
	{
		uint16_t thisRead = usb_vcp_get_rxdata(&usb_core_dev, buffer + len);
		if (thisRead != 0)
		{
			len += thisRead;
			timeout = millis();
		}
		else
		{
			if (millis() > (timeout + 2))
			{
				break;
			}
		}
	}
	return len;
}
uint16_t usb_vcp_write(uint8_t *buffer, uint16_t len)
{
	error_status status = ERROR;
	uint8_t timeout = 0;
	do
	{
		delay_ms(1);
		status = usb_vcp_send_data(&usb_core_dev, buffer, len);
		if (timeout++ > 100)
			return ERROR;
	} while (status != SUCCESS);

	return len;
}
void at32_flash_read(uint32_t address, uint8_t *buffer, uint32_t len)
{
	memcpy(buffer, (void *)address, len);
}
void at32_flash_write(uint32_t address, uint8_t *buffer, uint32_t len)
{
	flash_unlock();
	while (len--)
	{
		flash_byte_program(address++, *buffer++);
	}
	flash_lock();
}
typedef void (*iapfun)(void);
volatile iapfun jump2app;
void IAP_Load_APP(uint32_t appxaddr)
{
	SysTick->CTRL = 0; // stop systick
	usbd_disconnect(&usb_core_dev);
	crm_periph_clock_enable(CRM_USB_PERIPH_CLOCK, FALSE);
	crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, FALSE);
	nvic_irq_disable(USBFS_L_CAN1_RX0_IRQn);
	__disable_irq();
	jump2app = (iapfun) (* (volatile uint32_t *)(appxaddr + 4)); // 用户代码区第二个字为程序开始地址(复位地址)
	__set_MSP(*(volatile uint32_t *)appxaddr);
	__set_PSP(*(volatile uint32_t *)appxaddr);							//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
	__set_CONTROL(0);
	jump2app(); // 跳转到APP.
}
void at32_flash_sector_erase(uint32_t address)
{
	flash_sector_erase(address);
}
bl_info_t bl_info;
/**
 * @brief  main function.
 * @param  none
 * @retval none
 */
int main(void)
{

	/* config nvic priority group */
	nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);

	system_clock_config();

	/* select usb 48m clcok source */
	usb_clock48m_select(USB_CLK_HEXT);

	/* enable usb clock */
	crm_periph_clock_enable(CRM_USB_PERIPH_CLOCK, TRUE);
	crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE);

	/* enable usb interrupt */
	nvic_irq_enable(USBFS_L_CAN1_RX0_IRQn, 0, 0);

	/* usb core init */
	usbd_core_init(&usb_core_dev, USB, &cdc_class_handler, &cdc_desc_handler, 0);

	/* enable usb pull-up */
	usbd_connect(&usb_core_dev);

	systick_init();

	at32_mcu_who_am_i(&bl_info.whoAmI);

	bl_info.fw_info.pid = PID2INT(0, 0, 0, 0);
	bl_info.version = VESION2INT(2, 0, 0, 0);
	bl_info.appAddr = FLASH_APP_ADDR;
	bl_info.appSize = FLASH_APP_SIZE;
	bl_info.fwInfoAddr = FLASH_FWINFO_ADDR;
	bl_info.sectorSize = FLASH_SectorSize;
	bl_info.sectorCount = FLASH_SECTOR_COUNT;
	bl_info.read = usb_vcp_read;
	bl_info.write = usb_vcp_write;
	bl_info.millis = millis;
	bl_info.flashLock = flash_lock;
	bl_info.flashUnlock = flash_unlock;
	bl_info.flashSectorErase = at32_flash_sector_erase;
	bl_info.flashRead = at32_flash_read;
	bl_info.flashWrite = at32_flash_write;
	bl_info.jumpToAddr = IAP_Load_APP;
	bl_info.reset = nvic_system_reset;
	memcpy(bl_info.whoAmI.UID, (void *)MCU_UNIQUE_96BIT_ID_BASE_ADDR_31_0, 12);

	gpio_init_type gpioInitS;
	gpioInitS.gpio_pins = GPIO_PINS_2;
	gpioInitS.gpio_mode = GPIO_MODE_INPUT;
	gpioInitS.gpio_pull = GPIO_PULL_NONE;
	gpioInitS.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
	gpio_init(GPIOD, &gpioInitS);
	while (1)
	{
		/* get usb vcp receive data */
		// bl_info.pack.buffer_size = usb_vcp_get_rxdata(&usb_core_dev, bl_info.pack.buffer);
		if(gpio_input_data_bit_read(GPIOD, GPIO_PINS_2) != RESET)
		{
			// bl_info.write("hello world\r\n", 13);
			bl_info.synecd = true;
			// delay_ms(50);
		}
		bootloader_handle(&bl_info);
		/* send data to hardware usart */
		// if (data_len > 0)
		// {
		// usb_vcp_send_data(&usb_core_dev, usb_buffer, data_len);
		// }

		// if (millis() > (dt + 1000))
		// {
		// 	dt = millis();
		// 	// usb_vcp_send_data(&usb_core_dev, ">", 2);
		// 	bl_info.write("hello world\r\n", 13);
		// }
	}
}

/**
 * @brief  this function handles usart2  and linecoding config.
 * @param  linecoding: linecoding value
 * @retval none
 */
void usb_usart_config(linecoding_type linecoding)
{
	usart_stop_bit_num_type usart_stop_bit = 0;
	usart_data_bit_num_type usart_data_bit = 0;
	usart_parity_selection_type usart_parity_select = 0;

	/* enable the usart2 and gpio clock */
	crm_periph_clock_enable(CRM_USART2_PERIPH_CLOCK, FALSE);
	crm_periph_clock_enable(CRM_USART2_PERIPH_CLOCK, TRUE);

	/* stop bit */
	switch (linecoding.format)
	{
	case 0x0:
		usart_stop_bit = USART_STOP_1_BIT;
		break;
		/* to be used when transmitting and receiving data in smartcard mode */
	case 0x1:
		usart_stop_bit = USART_STOP_1_5_BIT;
		break;
	case 0x2:
		usart_stop_bit = USART_STOP_2_BIT;
		break;
	default:
		usart_stop_bit = USART_STOP_1_BIT;
		break;
	}

	/* parity */
	switch (linecoding.parity)
	{
	case 0x0:
		usart_parity_select = USART_PARITY_NONE;
		break;
	case 0x1:
		usart_parity_select = USART_PARITY_ODD;
		break;
	case 0x2:
		usart_parity_select = USART_PARITY_EVEN;
		break;
	/* hardware usart not support parity for mark and space */
	case 0x3:
	case 0x4:
		break;
	default:
		usart_parity_select = USART_PARITY_NONE;
		break;
	}

	if (USART_PARITY_NONE == usart_parity_select)
	{
		/* data bits */
		switch (linecoding.data)
		{
		/* hardware usart not support data bits for 5/6 */
		case 0x5:
		case 0x6:
		case 0x7:
			break;
		case 0x8:
			usart_data_bit = USART_DATA_8BITS;
			break;
		/* hardware usart not support data bits for 16 */
		case 0x10:
			break;
		default:
			break;
		}
	}
	else
	{
		/* data bits */
		switch (linecoding.data)
		{
		/* hardware usart not support data bits for 5/6 */
		case 0x5:
		case 0x6:
			break;
		case 0x7:
			usart_data_bit = USART_DATA_8BITS;
			break;
		case 0x8:
			usart_data_bit = USART_DATA_9BITS;
			break;
		/* hardware usart not support data bits for 16 */
		case 0x10:
			break;
		default:
			usart_data_bit = USART_DATA_8BITS;
			break;
		}
	}

	nvic_irq_enable(USART2_IRQn, 0, 0);

	/* configure usart2 param */
	usart_init(USART2, linecoding.bitrate, usart_data_bit, usart_stop_bit);
	usart_parity_selection_config(USART2, usart_parity_select);
	usart_transmitter_enable(USART2, TRUE);
	usart_receiver_enable(USART2, TRUE);

	/* enable usart2 interrupt */
	usart_interrupt_enable(USART2, USART_RDBF_INT, TRUE);
	usart_enable(USART2, TRUE);
}

/**
 * @brief  this function handles usb interrupt.
 * @param  none
 * @retval none
 */
void USBFS_L_CAN1_RX0_IRQHandler(void)
{
	usbd_irq_handler(&usb_core_dev);
}

/**
 * @brief  usb delay millisecond function.
 * @param  ms: number of millisecond delay
 * @retval none
 */
void usb_delay_ms(uint32_t ms)
{
	/* user can define self delay function */
	delay_ms(ms);
}

/**
 * @brief  usb delay microsecond function.
 * @param  us: number of microsecond delay
 * @retval none
 */
// void usb_delay_us(uint32_t us)
// {
// 	delay_us(us);
// }

/**
 * @}
 */

/**
 * @}
 */
